/*
 * The MIT License (MIT)
 *
 * Copyright (c) 2016 pjoc.pub, blademainer.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 *
 */

package pub.pjoc.pay.order.pattern;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * Parse pattern words.
 *
 * @author blademainer
 * @version 2016-09-17 01:59
 */
public abstract class PatternParser {
  private static final Logger logger = LoggerFactory.getLogger(PatternParser.class);

  public static PatternWord[] parsePatternWords(String pattern) throws PatternWordNotSupportException {
    ArrayList<PatternWord> patternWords = new ArrayList<>();
    // sort by number of index
    SortedMap<Integer, PatternWord> patternWordMap = new TreeMap<>(); //(o1, o2) -> o1 - o2

    PatternWord[] values = PatternWord.values();
    for (PatternWord value : values) {

      Integer[] indexes = findIndexes(pattern, value);
      SortedMap<Integer, PatternWord> sortedMap = handlePatternIndexes(value, indexes);
      if (sortedMap == null) {
        throw new PatternWordNotSupportException(pattern);
      }
      patternWordMap.putAll(sortedMap);
    }
    Collection<PatternWord> mapValues = patternWordMap.values();
    patternWords.addAll(mapValues);
    return patternWords.toArray(new PatternWord[patternWords.size()]);
  }

  private static SortedMap<Integer, PatternWord> handlePatternIndexes(PatternWord patternWord, Integer[] indexes) {
    SortedMap<Integer, PatternWord> patternWordMap = new TreeMap<>();
    for (int i = 0; i < indexes.length; i++) {
      Integer index = indexes[i];
      if (patternWord.isMustTogether()
          && i < indexes.length - 1 && index != indexes[i + 1] - 1) {
        logger.error("Error parse pattern word: {}, because pattern word must be all together. "
            + "index: {} and index: {} not together!", patternWord, index, indexes[i + 1]);
        return null;
      }
      patternWordMap.put(index, patternWord);
    }
    return patternWordMap;
  }

  private static Integer[] findIndexes(String pattern, PatternWord patternWord) {
    ArrayList<Integer> indexes = new ArrayList<>();
    String patternString = patternWord.getPattern();
    int index = -1;
    for (index = pattern.indexOf(patternString, index); index >= 0;
         index = pattern.indexOf(patternString, index + 1)) {
      indexes.add(index);
    }
    return indexes.toArray(new Integer[indexes.size()]);
  }


  public static boolean matches(String words) {
    String patternString = buildMatcherPattern();
    Pattern pattern = Pattern.compile(patternString);
    Matcher matcher = pattern.matcher(words);
    boolean matches = matcher.matches();
    if (!matches) {
      logger.warn("Words: {} doesn't matches our pattern: {}", words, pattern);
    } else {
      if (logger.isDebugEnabled()) {
        logger.debug("Words: {} matches our pattern: {}", words, pattern);
      }
    }
    return matches;
  }

  private static String buildMatcherPattern() {
    StringBuilder builder = new StringBuilder();
    builder.append("(");
    PatternWord[] values = PatternWord.values();
    for (int i = 0; i < values.length; i++) {
      builder.append(values[i].getPattern());
      if (i < values.length - 1) {
        builder.append("|");
      }
    }
    builder.append(")+");
    return builder.toString();
  }

  public static void main(String[] args) throws PatternWordNotSupportException {
    String pattern = "datemachineIIIIII";
    boolean datemachineIIIIII = matches(pattern);
    System.out.println(datemachineIIIIII);
    System.out.println("indexes===" + Arrays.toString(findIndexes(pattern, PatternWord
        .PATTERN_DATE)));
    System.out.println("indexes===" + Arrays.toString(findIndexes(pattern, PatternWord
        .PATTERN_MACHINE)));
    System.out.println("indexes===" + Arrays.toString(findIndexes(pattern, PatternWord
        .PATTERN_INDEX)));

    System.out.println(Arrays.toString(parsePatternWords(pattern)));

  }
}
